#include "lista.h"

/*~~~~~~~~~~~~~~~                               ~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~ DEFINICIÃ“N DE LAS ESTRUCTURAS ~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~                               ~~~~~~~~~~~~~~~*/

//DefiniciÃ³n de la estructura nodo.
typedef struct nodo{

	void* valor;

	struct nodo* prox;

}nodo_t;

//DefiniciÃ³n de la estructura lista enlazada.
struct lista{

	nodo_t* primero;

	nodo_t* ultimo;

	size_t largo;

};

//DefiniciÃ³n de la estructura iterador de lista enlazada.
struct lista_iter{

	nodo_t* actual;

	nodo_t* anterior;

};

/*~~~~~~~~~~~~~~~ IMPLEMENTACIÃ“N DEL TAD ~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~      LISTA ENLAZADA    ~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~                        ~~~~~~~~~~~~~~~*/

//Crea un nodo que contiene un dato recibido por parametro. Devuelve el nodo
//o NULL si no se lo pudo crear.
nodo_t* nodo_crear(void* dato){

	nodo_t* n=(nodo_t*)malloc(sizeof(nodo_t));

	if(!n) return NULL;

	n->valor=dato;

	n->prox=NULL;

	return n;

}

//Crea una lista enlazada vacÃ­a. Devuelve la lista o NULL si no la pudo crear.
lista_t* lista_crear(void){

	lista_t* l=(lista_t*)malloc(sizeof(lista_t));

	if(!l) return NULL;

	l->primero=l->ultimo=NULL;
	
	l->largo=0;

	return l;

}

//Devuelve true si la lista esta vacÃ­a o en su defecto. Si no se le pasa
//la lista por parÃ¡metro tambiÃ©n devuelve false.
bool lista_esta_vacia(const lista_t* l){

	if(!l) return false;

	return (l->largo==0)? true:false;

}

//Inserta un elemento recibido por parÃ¡metro al principio de la lista.
//Devuelve true si se pudo insertar correctamente o false en su defecto.
//Si la lista pasada por parÃ¡metro o elelemento no existen, o no
//se pudo crear el nodo para contener al elemento tambiÃ©n se devuelve false.
bool lista_insertar_primero(lista_t* l, void* dato){

	if(!l || !dato) return false;

	nodo_t* n=nodo_crear(dato);

	if(!n) return false;

	if(lista_esta_vacia(l))	l->primero=l->ultimo=n;
	
	else{

		n->prox=l->primero;

		l->primero=n;

	}

	(l->largo)++;

	return true;

}

//Inserta un elemento recibido por parÃ¡metro al final de la lista. Devuelve
//true si se pudo insertar correctamente o false en su defecto. Si la lista
//pasada por parÃ¡metro o elelemento no existen, o no se pudo crear el nodo
//para contener al elemento tambiÃ©n se devuelve false.
bool lista_insertar_ultimo(lista_t* l, void* dato){

	if(!l || !dato) return false;

	if(lista_esta_vacia(l)) return lista_insertar_primero(l,dato);

	nodo_t* n=nodo_crear(dato);

	if(!n) return false;

	(l->ultimo)->prox=n;

	l->ultimo=n;

	(l->largo)++;

	return true;

}

//Borra el primer elemento de una lista y devuelve su valor. Devuelve NULL si
//no se le pasÃ³ la lista por parametro.
void *lista_borrar_primero(lista_t* l){

	if(!l || lista_esta_vacia(l)) return NULL;

	void* valor=(l->primero)->valor;

	nodo_t* aux=(l->primero)->prox;

	free(l->primero);

	l->primero=aux;

	(l->largo)--;

	return valor;

}

//Devuelve el valor del primer elemento de la lista. Devuelve NULL si no se
//le paso la lista por parÃ¡metro o si la lista no tiene ningÃºn elemento.
void *lista_ver_primero(const lista_t* l){

	if(!l || lista_esta_vacia(l)) return NULL;

	return (l->primero)->valor;

}

//Devuelve el largo de la lista.
size_t lista_largo(const lista_t* l){

	if(!l) return 0;

	return l->largo;

}

//Destruye todos los nodos de lista y recibe una funciÃ³n para borrar los datos
//de cada nodo. Si no se le pasa funciÃ³n, no los destruye. Si no se le pasa
//la lista por parÃ¡metro no hace nada. Libera el espacio de memoria ocupado
//por la lista.
void lista_destruir(lista_t* l, void destruir_dato(void *)){

	if(!l) return;

	nodo_t* actual=l->primero;

	nodo_t* aux;

	while(actual){

		aux=actual->prox;

		if(destruir_dato) destruir_dato(actual->valor);

		free(actual);

		actual=aux;

	}

	free(l);

}

/*~~~~~~~~~~~~~~~   IMPLEMENTACIÃ“N DEL TAD   ~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~ ITERADOR DE LISTA ENLAZADA ~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~                            ~~~~~~~~~~~~~~~*/

//Crea un iterador de la lista enlazada recibida por parÃ¡metro. Si no se
//le pasa la lista, o no se pudo crear el iterador, devuelve NULL. En caso
//contrario, devuelve el iterador.
lista_iter_t* lista_iter_crear(const lista_t* list){
	
	if(!list) return NULL;

	lista_iter_t* iterador=(lista_iter_t*)malloc(sizeof(lista_iter_t));

	if(!iterador) return NULL;

	iterador->actual=list->primero;

	iterador->anterior=NULL;

	return iterador;

}

//El iterador avanza una posiciÃ³n sobre su lista. Si estÃ¡ al final de la
//lista, o no se le pasa la lista por parÃ¡metro, devuelve false. En caso
//contrario, devuelve true y se posiciona en el siguiente nodo de la lista.
bool lista_iter_avanzar(lista_iter_t* iterador){

	if(!iterador || lista_iter_al_final(iterador) ) return false;

	iterador->anterior=iterador->actual;

	iterador->actual=(iterador->actual)->prox;

	return true;

}

//Devuelve el valor del nodo sobre el cual esta parado el iterador.
//Devuelve NULL si no se le pasa el iterador por parÃ¡metro o el iterador
//esta parado sobre el nulo.
void* lista_iter_ver_actual(const lista_iter_t* iterador){

	if(!iterador || !iterador->actual) return NULL;

	return (iterador->actual)->valor;

}

//Decide si el iterador estÃ¡ parado sobre el final de la lista. Devuelve true
//si no lo estÃ¡ o si no se le paso iterador por parÃ¡metro y true si lo estÃ¡.
bool lista_iter_al_final(const lista_iter_t* iterador){

	if( (!iterador) || (iterador->actual) ) return false;

	return true;

}

//Destruye el iterador, si es que se lo pasÃ³ por parÃ¡metro.
void lista_iter_destruir(lista_iter_t* iterador){

	if(!iterador) return;

	free(iterador);

}

/*~~~~~~~~~~~~~~~  IMPLEMENTACIÃ“N DE LAS  ~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~  FUNCIONES QUE COMBINAN ~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~    LISTA CON ITERADOR   ~~~~~~~~~~~~~~~*/

//Inserta un elemento en la posiciÃ³n actual del iterador. Si no se le pasa
//dato o iterador o lista por parametrÃ³ o no se pudo crear el nodo para
//contener el dato, devuelve false. En caso contrario, devuelve true e
//inserta el elemento.
bool lista_insertar(lista_t* list, lista_iter_t* iterador, void *dato){

	if(!dato || !list || !iterador) return false;

	nodo_t* insert=nodo_crear(dato);

	if( !insert ) return false;

	if(lista_esta_vacia(list)){
		
		list->primero=list->ultimo=insert;
		
		iterador->actual=insert;
		
		(list->largo)++;
		
	}else if(lista_iter_al_final(iterador)){
		
		(iterador->anterior)->prox=insert;
		
		iterador->actual=list->ultimo=insert;
		
		(list->largo)++;
		
	}else if( (iterador->actual)==(list->primero) ){
		
		insert->prox=iterador->actual;
		
		list->primero=insert;
		
		iterador->actual=insert;
		
		(list->largo)++;
		
	}else{
		
		(iterador->anterior)->prox=insert;
		
		insert->prox=iterador->actual;
		
		iterador->actual=insert;
		
		(list->largo)++;
	
	}		

	return true;

}

//Borra el elemento en la posiciÃ³n actual del iterador. Si no se le pasa
//iterador o lista por parametrÃ³ o la lista esta vacÃ­a, devuelve NULL.
//En caso contrario, borra el elemento de la lista y lo devuelve.
void* lista_borrar(lista_t* list, lista_iter_t* iterador){

	if( !list || !iterador || lista_esta_vacia(list) || lista_iter_al_final(iterador) ) return NULL;
	
	void* valor=(iterador->actual)->valor;
	
	nodo_t* aux=iterador->actual;
	
	if(lista_largo(list)==1){
		
		list->ultimo=list->primero=iterador->actual=iterador->anterior=NULL;

		list->largo=0;
		
	}else if(iterador->actual==list->primero){
		
		iterador->actual=iterador->actual->prox;
		
		(list->largo)--;
		
		list->primero=iterador->actual;
		
	}else{
		
		iterador->actual=(iterador->actual)->prox;
		
		(iterador->anterior)->prox=iterador->actual;
		
		(list->largo)--;
		
	}
		
	free(aux);
	
	return valor;
	
}
